(setq my-pairs
      '(("\"" . "\"")
        ("(" . ")")
        ("[" . "]")
        ("'" . "'")
        ("{" . "}")
        ("<" . ">")))

(defun my-get-pair-point-forward (beg end)
  (let ((cnt 1)
        (reg (concat "\\("
                     (regexp-quote beg)
                     "\\)\\|\\("
                     (regexp-quote end)
                     "\\)"))
        (beg-end-equal (string= beg end)))
    (save-excursion
      (forward-char 1)
      (while (and (not (eobp)) (> cnt 0))
        (if (re-search-forward reg nil t)
            (progn
              (if (string= (match-string 1) beg)
                  (if beg-end-equal
                      (setq cnt 0)
                    (setq cnt (1+ cnt)))
                (setq cnt (1- cnt))))
          (end-of-buffer)))
      (if (= cnt 0) (point) nil))))

(defun my-get-pair-point-backward (beg end)
  (let ((cnt 1)
        (reg (concat "\\("
                     (regexp-quote beg)
                     "\\)\\|\\("
                     (regexp-quote end)
                     "\\)"))
        (beg-end-equal (string= beg end)))
    (save-excursion
      (forward-char -1)
      (while (and (not (bobp)) (> cnt 0))
        (if (re-search-backward reg nil t)
            (progn
              (if (string= (match-string 1) beg)
                  (if beg-end-equal
                      (setq cnt 0)
                    (setq cnt (1- cnt)))
                (setq cnt (1+ cnt))))
          (beginning-of-buffer)))
      (if (= cnt 0) (point) nil))))

(defun my-set-region (beg end)
  (push-mark beg t t)
  (goto-char end))

(defun wy-go-to-char (n char)
  "Move forward to Nth occurence of CHAR.
Typing `wy-go-to-char-key' again will move forwad to the next Nth
occurence of CHAR."
  (interactive "p\ncGo to char: ")
  (search-forward (string char) nil nil n)
  (while (char-equal (read-char)
					 char)
    (search-forward (string char) nil nil n))
  (setq unread-command-events (list last-input-event)))

(defun my-locate-near-str (str bound)
  (let ((pt (point))
        (bound-backward (max 0 (- (point) bound)))
        (bound-forward (min (point-max) (+ (point) bound)))
        pt-backward
        pt-forward)
    (setq pt-forward (search-forward str bound-forward t))
    (goto-char pt)
    (setq pt-backward (search-backward str bound-backward t))
    (goto-char pt)
    (cond
     ((and pt-forward pt-backward)
      (if (< (- pt-forward pt)
             (- pt pt-backward))
          pt-forward
        pt-backward))
     (pt-forward
      pt-forward)
     (pt-backward
      pt-backward)
     (t
      nil))))

(defun my-locate-str-forward (str bound)
  (save-excursion
    (search-forward str (min (+ (point) bound) (point-max)) t)))

(defun my-locate-str-backward (str bound)
  (save-excursion
    (search-backward str (max (- (point) bound) (point-min)) t)))

(defun my-pair-region-smart (char)
  (interactive "cInput pair character: ")
  (let* ((curr char)
         (str (char-to-string curr))
         beg end forward
         beg-end-equal)
    (dolist (pair my-pairs)
      (let ((b (car pair))
            (e (cdr pair)))
        (when (or (string-equal b str)
                  (string-equal e str))
          (setq beg b
                end e))))
    (when beg
      (setq beg-end-equal (string-equal beg end)
            forward (string-equal end str)
            mark-active nil)
      (while (char-equal curr char)
        (if beg-end-equal
            (let ((pt-near1 (my-locate-near-str str 1024))
                  pt-near2)
              (when pt-near1
                (if (< pt-near1 (point))
                    (progn
                      (setq pt-near1 (1+ pt-near1))
                      (goto-char pt-near1)
                      (setq pt-near2 (my-get-pair-point-backward beg end))
                      (when pt-near2
                        (my-set-region pt-near2 pt-near1)))
                  (setq pt-near1 (1- pt-near1))
                  (goto-char pt-near1)
                  (setq pt-near2
                        (my-get-pair-point-forward beg end))
                  (when pt-near2
                    (my-set-region pt-near1 pt-near2)))))
          (if forward
              (let ((pt-forward (my-locate-str-forward str 1024))
                    pt-backward)
                (when pt-forward
                  (goto-char pt-forward)
                  (setq pt-backward (my-get-pair-point-backward beg end))
                  (when pt-backward
                    (my-set-region pt-backward pt-forward))))
            (let ((pt-backward (my-locate-str-backward str 1024))
                  pt-forward)
              (when pt-backward
                (goto-char pt-backward)
                (setq pt-forward (my-get-pair-point-forward beg end))
                (when pt-forward
                  (my-set-region pt-forward pt-backward))))))
        (setq curr (read-char))))))

(global-set-key (kbd "<f12>") 'my-pair-region-smart)

(defun my-pair-region ()
  (interactive)
  (if mark-active
      (setq mark-active nil)
    (let ((prev (char-to-string (preceding-char)))
          (curr (char-to-string (following-char)))
          pt)
      (dolist (pair my-pairs)
        (let ((beg (car pair))
              (end (cdr pair)))
          (cond
           ((string= curr beg)
            (when (setq pt (my-get-pair-point-forward beg end))
              (my-set-region (point) pt)))
           ((string= prev end)
            (when (setq pt (my-get-pair-point-backward beg end))
              (my-set-region pt (point))))))))))

(provide 'my-pair-region)
